package com.hevs.samplewebapp.client.screenComponents;


import java.util.Date;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DecoratorPanel;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.datepicker.client.CalendarUtil;
import com.google.gwt.user.datepicker.client.DateBox;
import com.hevs.samplewebapp.client.bean.MedicalDataItem;
import com.hevs.samplewebapp.client.bean.StatisticsHelper;
import com.hevs.samplewebapp.client.bean.StatisticsProcessingHelper;
import com.hevs.samplewebapp.client.bean.TimeLaps;

/**
 * 
 * @author Thomas Rouvinez
 * @Date: 26.04.2012
 * 
 * Goal: Display and bake data for statistics. 
 *
 */
public class ScreenComponentStatistics extends Composite{

	// ------------------------------------------------------------------------------------------------
	// Variables.
	// ------------------------------------------------------------------------------------------------

	/**
	 * Main layout for the statistics.
	 */
	private DecoratorPanel decPanel = new DecoratorPanel();

	/**
	 * Title for the kind of statistics we display.
	 */
	private Label statisticsTitle = new Label("Global Statistics");
	
	/**
	 * Button to get the overall statistics.
	 */
	private Button global = new Button("Overall");
	
	/**
	 * Button to get the after breakfast statistics.
	 */
	private Button afterBreakfast = new Button("After Breakfast");
	
	/**
	 * Button to get the after lunch statistics.
	 */
	private Button afterLunch = new Button("After Lunch");
	
	/**
	 * Button to get the after dinner statistics.
	 */
	private Button afterDinner = new Button("After Dinner");
	
	/**
	 * FlexTable to display the statistics in the GUI.
	 */
	private FlexTable statisticsDisplay = new FlexTable();
	
	/**
	 * Array to store the buttons for the time ranges changes.
	 */
	private Button[] timeRangesButtons = new Button[5];
	
	/**
	 * Label to create spaces in the GUI.
	 */
	private Label space = new Label(" ");

	/**
	 * Currently selected category of medical data.
	 */
	private int selectedCategory = 0;
	
	/**
	 * Boolean value to know whether we display global or categorical statistics.
	 */
	private boolean isGlobal = true;
	
	/**
	 * Currently selected time range.
	 */
	private int selectedTimeLaps = 1;
	
	/**
	 * Boolean value to know whether the statistics are already baked.
	 */
	private boolean statisticsBaked = false;
	
	/**
	 * Boolean value to know whether we have the correct list of medical data.
	 */
	private boolean dataProcessed = false;

	/**
	 * Storage unit for the statistics to be baked.
	 */
	private StatisticsHelper[] stats;
	
	/**
	 * Array of medical data received from the web service.
	 */
	private MedicalDataItem[] medicalDataInput = new MedicalDataItem[0];
	
	/**
	 * Date we begin the time range with.
	 */
	private Date fromDate = null;
	
	/**
	 * Date we end the time range with.
	 */
	private Date toDate = null;

	// ------------------------------------------------------------------------------------------------
	// Constructor.
	// ------------------------------------------------------------------------------------------------

	/**
	 * Constructor for the screen component displaying the statistics.
	 */
	public ScreenComponentStatistics(){

		VerticalPanel mainPanel = new VerticalPanel();
		mainPanel.addStyleName("statPanel");
		
		// Define starting range.
		fromDate = new Date();
		toDate = new Date();
		CalendarUtil.addDaysToDate(toDate, (-7));

		// --------------------------------------------------------------------------------------------
		// Create the time range selection toolBar.
		// --------------------------------------------------------------------------------------------
		HorizontalPanel timeRangeSelection = new HorizontalPanel();

		for(int i = 0 ; i < timeRangesButtons.length ; ++i){
			final int cpt = i;

			// Set button's text.
			timeRangesButtons[i] = new Button(TimeLaps.get(i).getName());
			timeRangesButtons[i].setWidth("120px");
			timeRangesButtons[i].setStyleName("gwt-ButtonRangeTab");

			if(i < 4){
				// Add each button a listener.
				timeRangesButtons[i].addClickHandler(new ClickHandler() {
					@Override
					public void onClick(ClickEvent event) {
						// Change selected range.
						selectedTimeLaps = cpt;
						fromDate = new Date();
						toDate = new Date();
						
						switch (cpt) {
							
						case 1:
							CalendarUtil.addDaysToDate(toDate, (-7));
							break;
							
						case 2:
							CalendarUtil.addDaysToDate(toDate, (-30));
							break;
							
						case 3:
							CalendarUtil.addDaysToDate(toDate, (-90));
							break;

						default:
							break;
						}
						
						displayDates();
						displayStatistics();
					}
				});
			}
			else{
				// Define the custom time range button.
				timeRangesButtons[i].addClickHandler(new ClickHandler() {
					@Override
					public void onClick(ClickEvent event) {
						// Change selected range.
						selectedTimeLaps = cpt;

						pickDates();
						displayDates();
						displayStatistics();
					}
				});
			}

			// Add button to the flexTable.
			timeRangeSelection.add(timeRangesButtons[i]);

			// Add the buttons' container to the mainPanel.
			mainPanel.add(timeRangeSelection);
		}

		// --------------------------------------------------------------------------------------------
		// Statistics information.
		// --------------------------------------------------------------------------------------------

		Label spaceTopStats = new Label(" ");
		spaceTopStats.setHeight("20px");
		Label spaceMidStats = new Label(" ");
		spaceMidStats.setHeight("10px");

		// Setup the new flexTable for statistics display.
		statisticsDisplay.setWidget(0, 0, spaceTopStats);
		statisticsDisplay.setWidget(2, 0, statisticsTitle);
		statisticsDisplay.setWidget(2, 1, spaceMidStats);

		statisticsDisplay.setWidget(1, 2, new HTML("Min"));
		statisticsDisplay.setWidget(1, 4, new HTML("Mean"));
		statisticsDisplay.setWidget(1, 6, new HTML("Max"));
		statisticsDisplay.setWidget(1, 8, new HTML("Median"));
		statisticsDisplay.setWidget(1, 10, new HTML("Standard Deviation"));

		statisticsDisplay.setWidget(3, 0, new HTML("Diastolic Pressure"));
		statisticsDisplay.setWidget(4, 0, new HTML("Systolic Pressure"));
		statisticsDisplay.setWidget(5, 0, new HTML("Blood Sugar"));
		statisticsDisplay.setWidget(6, 0, new HTML("Humalog"));
		statisticsDisplay.setWidget(7, 0, new HTML("Novorapid"));
		statisticsDisplay.setWidget(8, 0, new HTML("Weight"));
		statisticsDisplay.setWidget(9, 0, new HTML("Pulse Rate"));

		// Add the statistics' table to the mainPanel.
		statisticsDisplay.setCellSpacing(5);
		mainPanel.add(statisticsDisplay);

		// --------------------------------------------------------------------------------------------
		// Create general options toolBar.
		// --------------------------------------------------------------------------------------------

		// Add radio buttons for statistics' type.
		FlexTable options = new FlexTable();
		options.setBorderWidth(0);
		options.setCellPadding(0);
		options.setCellSpacing(0);

		space.setHeight("20px");

		// Add buttons to choose the category of data.
		global.setWidth("150px");
		afterBreakfast.setWidth("150px");
		afterLunch.setWidth("150px");
		afterDinner.setWidth("150px");
		
		global.setStyleName("gwt-ButtonRangeTab");
		afterBreakfast.setStyleName("gwt-ButtonRangeTab");
		afterLunch.setStyleName("gwt-ButtonRangeTab");
		afterDinner.setStyleName("gwt-ButtonRangeTab");
		
		// Display dates.
		displayStatistics();
		
		// Add Listeners.
		setStatsListeners();

		options.setWidget(0, 0, global);
		options.setWidget(0, 1, afterBreakfast);
		options.setWidget(0, 2, afterLunch);
		options.setWidget(0, 3, afterDinner);

		mainPanel.add(space);
		mainPanel.add(options);

		// --------------------------------------------------------------------------------------------
		// Initialize the widget.
		// --------------------------------------------------------------------------------------------
		
		dataProcessed = false;

		decPanel.setWidget(mainPanel);
		initWidget(decPanel);
	}

	// ------------------------------------------------------------------------------------------------
	// Methods.
	// ------------------------------------------------------------------------------------------------

	/**
	 * Method to display statistics in the GUI.
	 */
	public void displayStatistics(){
		// Bake Statistics if not already baked.
		if(statisticsBaked == false){
			bakeStatistics(medicalDataInput);
			statisticsBaked = true;
		}

		// Display Data.
		if(isGlobal == true){
			displayGlobalStats();
		}
		else{
			displayCategoryStats();
		}
	}

	/**
	 *  Method to attribute listeners to the buttons of the statistics panel.
	 */
	private void setStatsListeners(){
		global.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				isGlobal = true;
				global.setStyleName("buttonSelected");
				afterBreakfast.setStyleName("gwt-Button");
				afterDinner.setStyleName("gwt-Button");
				afterLunch.setStyleName("gwt-Button");
				
				// Display Data.
				displayStatistics();
			}
		});
		
		afterBreakfast.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				selectedCategory = 0;
				isGlobal = false;

				global.setStyleName("gwt-Button");
				afterBreakfast.setStyleName("buttonSelected");
				afterDinner.setStyleName("gwt-Button");
				afterLunch.setStyleName("gwt-Button");
				// Display Data.
				displayStatistics();
			}
		});
		
		afterDinner.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				selectedCategory = 1;
				isGlobal = false;

				global.setStyleName("gwt-Button");
				afterBreakfast.setStyleName("gwt-Button");
				afterDinner.setStyleName("buttonSelected");
				afterLunch.setStyleName("gwt-Button");
				// Display Data.
				displayStatistics();
			}
		});
		
		afterLunch.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				selectedCategory = 2;
				isGlobal = false;

				global.setStyleName("gwt-Button");
				afterBreakfast.setStyleName("gwt-Button");
				afterDinner.setStyleName("gwt-Button");
				afterLunch.setStyleName("buttonSelected");
				// Display Data.
				displayStatistics();
			}
		});
	}

	/**
	 * Method to bake the data into statistics.
	 * @param data
	 */
	public void bakeStatistics(MedicalDataItem[] data){
		if(data != null){
			// Bake statistics.
			stats = new StatisticsHelper[5];

			//StatisticsProcessingHelper statsProcessing = new StatisticsProcessingHelper(data);
			stats[0] = new StatisticsProcessingHelper(data).getStatistics(0); 	// Daily.
			stats[1] = new StatisticsProcessingHelper(data).getStatistics(7); 	// Weekly.
			stats[2] = new StatisticsProcessingHelper(data).getStatistics(30);	// Monthly.
			stats[3] = new StatisticsProcessingHelper(data).getStatistics(90);	// Quarterly.
			// stats[5] reserved for custom global statistics.
		}
	}

	/**
	 * Method to display global statistics.
	 */
	private void displayGlobalStats(){
		int[] link = {1, 0, 2, 3, 4, 5, 6};

		for(int i = 0 ; i < 7 ; ++i){
			statisticsDisplay.setWidget(i+3, 2, formatData(stats[selectedTimeLaps].getMin()[link[i]]));
			statisticsDisplay.setWidget(i+3, 4, formatData(stats[selectedTimeLaps].getMean()[link[i]]));
			statisticsDisplay.setWidget(i+3, 6, formatData(stats[selectedTimeLaps].getMax()[link[i]]));
			statisticsDisplay.setWidget(i+3, 8, formatData(stats[selectedTimeLaps].getMedian()[link[i]]));
			statisticsDisplay.setWidget(i+3, 10, formatData(stats[selectedTimeLaps].getStandardDeviation()[link[i]]));
		}
	}

	/**
	 * Method to display category-related statistics.
	 */
	private void displayCategoryStats(){
		
		// Clean all the previous values from the global statistics.
		for(int i = 0 ; i < 7 ; ++i){
			statisticsDisplay.setWidget(i+3, 2, new HTML(""));
			statisticsDisplay.setWidget(i+3, 4, new HTML(""));
			statisticsDisplay.setWidget(i+3, 6, new HTML(""));
			statisticsDisplay.setWidget(i+3, 8, new HTML(""));
			statisticsDisplay.setWidget(i+3, 10, new HTML(""));
		}
		
		// Display required values.
		for(int i = 5 ; i < 8 ; ++i){
			statisticsDisplay.setWidget(i, 2, formatData(stats[selectedTimeLaps].getCategoryStatistics()[i-5][selectedCategory].getMinCategory()));
			statisticsDisplay.setWidget(i, 4, formatData(stats[selectedTimeLaps].getCategoryStatistics()[i-5][selectedCategory].getMeanCategory()));
			statisticsDisplay.setWidget(i, 6, formatData(stats[selectedTimeLaps].getCategoryStatistics()[i-5][selectedCategory].getMaxCategory()));
			statisticsDisplay.setWidget(i, 8, formatData(stats[selectedTimeLaps].getCategoryStatistics()[i-5][selectedCategory].getMedianCategory()));
			statisticsDisplay.setWidget(i, 10, formatData(stats[selectedTimeLaps].getCategoryStatistics()[i-5][selectedCategory].getStandardDeviationCategory()));
		}
	}
	
	/**
	 * Pre-display data treatment.
	 * @param value
	 * @return a new formatted HTML object to display the values.
	 */
	private HTML formatData(double value){

		HTML html = new HTML("" + NumberFormat.getFormat(".00").format(value));

		if(value == 999.0 || value == -999.0 || Double.isNaN(value) == true){
			html.setText("  -  ");
		}

		return html;
	}

	/**
	 * Method to create dialog box for custom dates picking.
	 */
	private void pickDates()
	{
		final DialogBox box=new DialogBox();
		box.setGlassEnabled(true);
		box.setAnimationEnabled(true);
		box.setText("Choose a date range");

		// Layout of the content
		VerticalPanel mainLayout=new VerticalPanel();
		HorizontalPanel buttonsLayout=new HorizontalPanel();

		final DateTimeFormat dateFormat=DateTimeFormat.getFormat("dd.MM.yyyy");
		Button apply=new Button("Apply");
		Button reset=new Button("Reset");
		Button cancel=new Button("Cancel");

		final DateBox fromBox=new DateBox();
		fromBox.setFormat(new DateBox.DefaultFormat(dateFormat));
		fromBox.setValue(new Date());
		fromBox.getDatePicker().setCurrentMonth(fromBox.getValue());

		final DateBox toBox=new DateBox();
		toBox.setFormat(new DateBox.DefaultFormat(dateFormat));
		toBox.setValue(new Date());
		toBox.getDatePicker().setCurrentMonth(toBox.getValue());

		FlexTable tableLayout=new FlexTable();
		tableLayout.setHTML(1,0,"From:");
		tableLayout.setWidget(1,1,fromBox);
		tableLayout.setHTML(2,0,"To:");
		tableLayout.setWidget(2,1,toBox);

		apply.setWidth("100%");
		reset.setWidth("100%");
		cancel.setWidth("100%");

		// Listeners
		apply.addClickHandler(new ClickHandler()
		{
			@Override
			public void onClick(ClickEvent event)
			{
				fromDate=fromBox.getValue();
				toDate=toBox.getValue();

				//Compute specific data.
				stats[4] = new StatisticsProcessingHelper(medicalDataInput, fromDate, toDate).getCustomStatistics();	// Custom date range.	
				statisticsBaked = true;
				
				displayDates();
				
				box.hide();
				displayStatistics();
			}                      
		});

		reset.addClickHandler(new ClickHandler()
		{
			@Override
			public void onClick(ClickEvent event)
			{
				fromDate=null;
				toDate=null;
				fromBox.setValue(new Date());
				fromBox.getDatePicker().setCurrentMonth(fromBox.getValue());
				toBox.setValue(new Date());
				toBox.getDatePicker().setCurrentMonth(toBox.getValue());

				displayDates();
				box.hide();

				selectedTimeLaps = 0;	// Back to today's date.
				displayStatistics();
			}                       
		});

		cancel.addClickHandler(new ClickHandler()
		{
			@Override
			public void onClick(ClickEvent event)
			{		
				box.hide();
			}       
		});

		buttonsLayout.add(apply);
		buttonsLayout.add(reset);
		buttonsLayout.add(cancel);
		buttonsLayout.setSpacing(10);

		mainLayout.add(tableLayout);
		mainLayout.add(buttonsLayout);
		mainLayout.setSpacing(5);
		mainLayout.setCellHorizontalAlignment(buttonsLayout,VerticalPanel.ALIGN_CENTER);

		box.setWidget(mainLayout);
		box.show();
		box.center();

	}
	
	/**
	 * Method to display dates currently selected.
	 */
	private void displayDates() {
		
		// Create date formatter for easy printing of dates.
		final DateTimeFormat dateFormat=DateTimeFormat.getFormat("dd.MM.yyyy");
		
		// Apply new dates to the GUI in the flexTable.
		statisticsDisplay.setWidget(0, 0, new HTML("From: " + dateFormat.format(fromDate) + " to: " 
				+ dateFormat.format(toDate)));
	} 

	// ------------------------------------------------------------------------------------------------
	// Getters - setters.
	// ------------------------------------------------------------------------------------------------

	/**
	 * Getter for the boolean value to know if the statistics have been baked or not.
	 * @return boolean value if the statistics have been baked or not.
	 */
	public boolean isStatisticsBaked() {
		return statisticsBaked;
	}

	/**
	 * Getter for the boolean value to know if we have the latest data.
	 * @return boolean value if data has been processed or not.
	 */
	public boolean getDataProcessed(){
		return dataProcessed;
	}

	/**
	 * Setter for the boolean value to know if we have the latest data.
	 * @param dataProcessed
	 */
	public void setDataProcessed(boolean dataProcessed){
		this.dataProcessed = dataProcessed;
	}

	/**
	 * Setter the boolean value to know if we have already baked the statistics.
	 * @param statisticsBaked
	 */
	public void setStatisticsBaked(boolean statisticsBaked) {
		this.statisticsBaked = statisticsBaked;
	}

	/**
	 * Setter for the medical data recovered from the web service.
	 * @param medicalDataInput
	 */
	public void setMedicalDataInput(MedicalDataItem[] medicalDataInput) {
		// Re-bake the statistics.
		this.medicalDataInput = medicalDataInput;
		bakeStatistics(this.medicalDataInput);
	}
}
